<template>
  <div class="dom-carousel" ref="root" :class="`dom-carousel-${direction}`"
      @mouseenter="handleMouseenter" @mouseleave="handleMouseleave">
    <slot></slot>
  </div>
</template>

<script>
import {defineComponent} from 'vue';

/**
 * 优点：相较于修改Scroll轮播方式的缺点：
 *  - 使用的同时注意CarouselItem的传参，同时需要设置两个组件的参数，且需要注意二者相同参数保持一致
 *  - CarouselItem中绑定的事件需要通过anewAddEvent重新手动绑定
 */
export default defineComponent({
  name: 'DomCarousel',
  /**
   * 修改DOM的轮播方式无需关注手动移动滚动条的相关问题，carousel事件的参数永远为轮播到的组索引，索引编号从0开始
   */
  emits: ['carousel'],
  props: {
    direction: {
      type: String,
      default: 'y',
      validator(value) {
        return ['x', 'y'].includes(value);
      }
    },
    play: {
      type: Boolean,
      default: true,
    },
    mouseMove: {
      type: Boolean,
      default: true,
    },
    /**
     * 不能太快，>=2000
     */
    cycleMs: {
      type: Number,
      default: 4000,
      validator(value) {
        return value >= 2000;
      }
    },
    /**
     * 必须为首行或首列元素整数倍，首行或首列与轮播方向相关，同时注意每个轮播项的宽高保持一致
     */
    numInGroup: {
      type: Number,
      required: true,
      validator(value) {
        return value > 0;
      }
    },
    anewAddEvent: {
      type: Function,
      default: () => {
      }
    }
  },
  data() {
    return {
      carouselInterval: null,
      currentGroupIndex: 0,
    };
  },
  watch: {
    play: {
      immediate: true,
      handler(newVal) {
        if (newVal) {
          this.$nextTick(() => {
            this.startCarouselInterval();
          });
        } else {
          this.endCarouselInterval();
        }
      }
    }
  },
  beforeUnmount() {
    this.endCarouselInterval();
  },
  methods: {
    startCarouselInterval() {
      if (this.carouselInterval) return;

      // 控制轮播时间
      let cycleMs = this.cycleMs;
      if (cycleMs < 2000) {
        cycleMs = 2000;
      }

      // 轮播组内元素数量默认为1
      let numInGroup = this.numInGroup;
      if (numInGroup <= 0) {
        numInGroup = 1;
      }

      const el = this.$refs.root;
      this.carouselInterval = setInterval(() => {
        // 被轮播掉的滚动子元素添加到滚动容器尾部，若该元素存在事件需要通过JS重新添加
        const moveItems = Array.from(el.children).slice(0, numInGroup);
        moveItems.forEach(item => {
          const clonedChild = item.cloneNode(true);
          el.appendChild(clonedChild);

          // JS中未找到好的获取元素所有事件的方式，所以此处需要外部手动添加
          this.anewAddEvent(clonedChild);
        });

        // 为滚动子元素添加消失动画
        Array.from(el.children).forEach(item => {
          if (this.direction === 'x') {
            item.setAttribute('class', item.className + ' item-move-left');
          }

          if (this.direction === 'y') {
            item.setAttribute('class', item.className + ' item-move-top');
          }
        });

        // 等待消失动画执行完毕
        setTimeout(() => {
          // 移除滚动子元素消失动画
          Array.from(el.children).forEach(item => {
            let className = item.className.replaceAll(' item-move-left', '');
            className = className.replaceAll(' item-move-top', '');
            item.setAttribute('class', className);
          });

          // 真正删除被轮播掉的滚动子元素
          moveItems.forEach(item => {
            el.removeChild(item);
          });

          // 当前组索引更新，索引从0开始编号
          this.currentGroupIndex++;
          const total = Math.ceil(Array.from(el.children).length / this.numInGroup);
          if (this.currentGroupIndex >= total) {
            this.currentGroupIndex = 0;
          }
          this.$emit('carousel', this.currentGroupIndex);
        }, 1000);
      }, cycleMs);
    },
    endCarouselInterval() {
      if (!this.carouselInterval) return;

      clearInterval(this.carouselInterval);
      this.carouselInterval = null;
    },
    handleMouseenter() {
      if (!this.mouseMove) return;

      this.endCarouselInterval();
    },
    handleMouseleave() {
      if (!this.mouseMove) return;

      this.startCarouselInterval();
    },
  }
});
</script>

<style scoped>
.dom-carousel {
  @apply h-full w-full;
}

.dom-carousel-x {
  @apply flex flex-col flex-wrap gap-x-3 gap-y-3 overflow-x-auto;

  &::-webkit-scrollbar { /* 滚动条整体样式 */
    height: 6px;
    background-color: transparent;
  }

  &::-webkit-scrollbar-thumb { /* 滚动条里面小方块 */
    height: 6px;
    background: #7D7E7D;
    border-radius: 6px;
  }
}

.dom-carousel-y {
  @apply flex flex-wrap gap-x-3 gap-y-3 overflow-y-auto;

  &::-webkit-scrollbar { /* 滚动条整体样式 */
    width: 4px;
    background-color: transparent;
  }

  &::-webkit-scrollbar-thumb { /* 滚动条里面小方块 */
    width: 4px;
    background: #7D7E7D;
    border-radius: 2px;
  }
}
</style>